// @Author EthanScriptOn
// @Desc
package detect

import (
	"fmt"
	"gitee.com/fatzeng/srf_switch_basic_components/switch_log"
	"gitee.com/fatzeng/srf_switch_basic_components/util"
	"golang.org/x/sync/errgroup"
	"sync"
	"time"
)

var BackendHealthChecker = GenerateResourceChecker(3 * time.Second)

type ResourceChecker struct {
	sync.RWMutex
	resources map[string]*ResourceMonitor
	ticker    *time.Ticker
	stop      chan struct{}
}

func GenerateResourceChecker(interval time.Duration) *ResourceChecker {
	rc := &ResourceChecker{
		resources: make(map[string]*ResourceMonitor),
		stop:      make(chan struct{}),
	}
	rc.ticker = time.NewTicker(interval)
	go rc.checkResources()
	return rc
}

func (r *ResourceChecker) AddResource(monitor *ResourceMonitor) {
	if _, ok := r.resources[monitor.Name]; ok || monitor == nil || !monitor.IsValid() {
		return
	}
	if monitor.IsTimeConsuming {
		monitor.Monitor = func(monitorCalled *ResourceMonitor) error {
			group := errgroup.Group{}
			group.Go(func() (err error) {
				defer util.HandleErr(&err)
				err = monitor.Monitor(monitorCalled)
				return
			})
			return group.Wait()
		}
	}
	_ = util.LockWithSync(r.RLock, r.RUnlock, func() (err error) {
		r.resources[monitor.Name] = monitor
		return
	})
}

func (r *ResourceChecker) checkResources() {
	defer func() {
		if err := recover(); err != nil {
			switch_log.Logger().Error("Recovered in checkResources: ", err)
			go r.checkResources()
		}
	}()

	for {
		select {
		case <-r.ticker.C:
			_ = util.LockWithSync(r.Lock, r.Unlock, func() (err error) {
				for _, check := range r.resources {
					err := check.Monitor(check)
					if err != nil {
						switch_log.Logger().Error(fmt.Sprintf("Resource '%s' monitor failed: %v", check.Name, err))
					}
				}
				return
			})
		case <-r.stop:
			switch_log.Logger().Warn("Resource detection has exited")
			return
		}
	}
}

func (r *ResourceChecker) Stop() {
	r.ticker.Stop()
	close(r.stop)
}
